
#include "Term.h"
#include "SemanticUtil/Binding.h"
#include "FwdInfer/Statement.h"
#include <string>
#include <list>
#include <map>
#include <iostream>
#include <sstream>
#include <QtCore>

#ifndef _PREDICATE_H_
#define _PREDICATE_H_


class Predicate{

public:
	std::string predicateName;
	int arguments;
	Term argument1;
	Term argument2;
	Term argument3;
	Term predicateNode;
	short coordinate[3];

public:
	Predicate(){
		Term null_term("Null");
		predicateName="Null";
		argument1=null_term;
		argument2=null_term;
		argument3=null_term;
		if(!predicateName.compare("subClass")){
			predicateNode.setName("http://www.w3.org/2000/01/rdf-schema#subClassOf");
			predicateNode.setType(RDFS_ATOM);
		}
		else if(!predicateName.compare("subProperty")){
			predicateNode.setName("http://www.w3.org/2000/01/rdf-schema#subPropertyOf");
			predicateNode.setType(RDFS_ATOM);
		}
		else if(!predicateName.compare("type")){
			predicateNode.setName("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
			predicateNode.setType(RDFS_ATOM);
		}else {
			predicateNode=argument2;
		}
	}
	Predicate(std::string name,Term arg1,Term arg2){
		predicateName = name;

		if(!predicateName.compare("subClass")){
			predicateNode.setName("http://www.w3.org/2000/01/rdf-schema#subClassOf");
			predicateNode.setType(RDFS_ATOM);
		}
		else if(!predicateName.compare("subProperty")){
			predicateNode.setName("http://www.w3.org/2000/01/rdf-schema#subPropertyOf");
			predicateNode.setType(RDFS_ATOM);
		}
		else if(!predicateName.compare("type")){
			predicateNode.setName("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
			predicateNode.setType(RDFS_ATOM);
		}else {
			predicateNode=argument2;
		}

		argument1 = arg1;
		argument2 = arg2;
		arguments = 2;
	}
	Predicate(std::string name,Term arg1,Term arg2, Term arg3){
		predicateName = name;
		argument1 = arg1;
		argument2 = arg2;
		argument3 = arg3;
		arguments = 3;
	}
	int getNumOfArguments(){
		return arguments;
	}
	void setNumOfArguments(int i){
		arguments = i;
	}
	Term getArgument1(){
		return argument1;
	}
	Term getArgument2(){
		return argument2;
	}
	Term getArgument3(){
		return argument3;
	}
	void setArgument1(Term arg1){
		argument1 = arg1;
	}
	void setArgument2(Term arg2){
		argument2= arg2;
	}
	void setArgument3(Term arg3){
		argument3 = arg3;
	}
	std::string getName(){
		return predicateName;
	}
	void setName(std::string n){
		predicateName = n;
	}

	std::list<std::string> getVariablesNames(){
		std::list<std::string> vars;
		if(argument1.getType()==RDFS_VARIABLE)
			vars.push_back(argument1.getName());
		if(argument2.getType()==RDFS_VARIABLE)
			vars.push_back(argument2.getName());
		if(argument3.getType()==RDFS_VARIABLE)
			vars.push_back(argument3.getName());
		return vars;
	}

	std::string iToString(int i){
		std::ostringstream i_s;
		i_s << i;
		return i_s.str();
	}

	std::string toString(){
		std::string info;
		toCoordinate();
		info+=" Coordinate:[" + iToString(coordinate[0]) + "," + iToString(coordinate[1])+"," + iToString(coordinate[2]) + "]";

		if(arguments==2)
			return predicateName + "(" + argument1.getName() + "," + argument2.getName() + ")"+ info;
		return predicateName + "(" + argument1.getName() + "," + argument2.getName() + "," + argument3.getName() + ")" +info;
	}

	Predicate renamePredicateVars(int recursion){
		Predicate p;
		std::cout << "========================" << std::endl;
		if(this->arguments==2){
			p.predicateName=predicateName;
			p.argument1=argument1;
			p.argument2=argument2;
			p.arguments=2;
		}else if(this->arguments==3){
			p.predicateName=predicateName;
			p.argument1=argument1;
			p.argument2=argument2;
			p.argument3=argument3;
			p.arguments=3;
		}
		std::cout << "Predicate to rename: " << p.toString() << std::endl;
		if(p.argument1.getType()==RDFS_VARIABLE){
			Term v1(p.argument1.getName()+"·");
			for (int i=2;i<recursion;i++)
				v1.setName(v1.getName()+"·");
			p.setArgument1(v1);
		}
		if(p.argument2.getType()==RDFS_VARIABLE){
			Term v2(p.argument2.getName()+"·");
			for (int i=2;i<recursion;i++)
				v2.setName(v2.getName()+"·");
			p.setArgument2(v2);
		}
		if(p.argument3.getName()!="Null" && p.argument3.getType()==RDFS_VARIABLE){
			Term v3(p.argument3.getName()+"·");
			for (int i=2;i<recursion;i++)
				v3.setName(v3.getName()+"·");
			p.setArgument3(v3);
		}
		std::cout << "Predicate renamed: " << p.toString() << std::endl;
		return p;
	}
	Predicate applySubstitution(std::map<std::string, Term> sub){
		if(this->argument1.getType()==RDFS_VARIABLE && sub.count(argument1.getName())){
			this->argument1 = sub[argument1.getName()];
//			std::cout << "applySubstitution arg1: " <<this->argument1.getName() << " " << sub[argument1.getName()].getName() << " ";
		}
		if(this->argument2.getType()==RDFS_VARIABLE && sub.count(argument2.getName())){
			this->argument2 = sub[argument2.getName()];
//			std::cout << "applySubstitution arg2: " <<this->argument2.getName()<< " " << sub[argument2.getName()].getName() << " ";
		}
		if(this->argument3.getName()!="Null" &&  this->argument3.getType()==RDFS_VARIABLE && sub.count(argument3.getName())){
			this->argument3 = sub[argument3.getName()];
//			std::cout << "applySubstitution arg3: " <<this->argument3.getName()<< " " << sub[argument3.getName()].getName() << " ";
		}
		return *this;
	}

	bool contains(std::string key){
		if((!argument1.getName().compare(key))||(!argument2.getName().compare(key)) ||
				(argument3.getName() !="Null" && (!argument3.getName().compare(key))))
			return true;
		else
			return false;
	}

	bool operator==(Predicate p){
		if(this->arguments==2){
			if(arguments==p.arguments && argument1.getName()==p.argument1.getName()&& argument2.getName()==p.argument2.getName()){
				return true;
			}else{
				return false;
			}
		}else if(this->arguments==3){
			if(arguments==p.arguments && argument1.getName()==p.argument1.getName()&& argument2.getName()==p.argument2.getName() && argument3.getName()==p.argument3.getName()){
				return true;
			}else{
				return false;
			}
		}else {
			return false;
		}
	}
	Predicate& operator=(Predicate p){
		arguments=p.arguments;
		argument1=p.argument1;
		argument2=p.argument2;
		argument3=p.argument3;
		predicateName=p.predicateName;
		return *this;
	}
	std::string toQuery(){
		std::string subquery_cmd="SELECT ";
		std::string subj_ele;
		std::string pred_ele;
		std::string obj_ele;
		if(this->arguments==2){
			if(argument1.getType()==RDFS_VARIABLE){//variable
				subquery_cmd+= argument1.getName()+" ";
				subj_ele=argument1.getName();
			}else{
				subj_ele="<"+argument1.getName()+">";
			}
			if(argument2.getType()==RDFS_VARIABLE){
				subquery_cmd+= argument2.getName()+" ";
				obj_ele=argument2.getName();
			}else{
				obj_ele="<"+argument2.getName()+">";
			}
			subquery_cmd+="WHERE {";
			subquery_cmd+=subj_ele+" <"+predicateName + "> " +obj_ele+"}";
		}else{
			if(argument1.getType()==RDFS_VARIABLE){
				subquery_cmd+= argument1.getName()+" ";
				subj_ele=argument1.getName();
			}else{
				subj_ele="<"+argument1.getName()+">";
			}
			if(argument2.getType()==RDFS_VARIABLE){
				subquery_cmd+= argument2.getName()+" ";
				pred_ele=argument2.getName();
			}else{
				pred_ele="<"+argument2.getName()+">";
			}
			if(argument3.getType()==RDFS_VARIABLE){
				subquery_cmd+= argument3.getName()+" ";
				obj_ele=argument3.getName();
			}else{
				obj_ele="<"+argument3.getName()+">";
			}
			subquery_cmd+="WHERE {";
			subquery_cmd+=subj_ele+ " " + pred_ele+ " " + obj_ele+"}";
		}
		return subquery_cmd;
	}


	std::string toAsk(){
		std::string subquery_cmd="ASK {";
		std::string subj_ele;
		std::string pred_ele;
		std::string obj_ele;
		if(this->arguments==2){
			subj_ele="<"+argument1.getName()+">";
			obj_ele="<"+argument2.getName()+">";
			subquery_cmd+=subj_ele+" <"+predicateName + "> " +obj_ele+"}";
		}else{
			subj_ele="<"+argument1.getName()+">";
			pred_ele="<"+argument2.getName()+">";
			obj_ele="<"+argument3.getName()+">";
			subquery_cmd+=subj_ele+ " " + pred_ele+ " " + obj_ele+"}";
		}
		return subquery_cmd;
	}

	short *toCoordinate(){
		int node_len =3;
		QString subjectQS;
		QString predicateQS;
		QString objectQS;
		QByteArray subjectQA;
		QByteArray predicateQA;
		QByteArray objectQA;
		short s_Co;
		short p_Co;
		short o_Co;
		short s_p_Co;
		short s_o_Co;
		short p_o_Co;

		subjectQS = QString::fromLocal8Bit(argument1.getName().c_str());
		subjectQA = QCryptographicHash::hash(subjectQS.toAscii(),QCryptographicHash::Sha1 );

		if(arguments==3){
			predicateQS = QString::fromLocal8Bit(argument2.getName().c_str());
			predicateQA = QCryptographicHash::hash(predicateQS.toAscii(),QCryptographicHash::Sha1 );
		}else{
			predicateQS = QString::fromLocal8Bit(predicateName.c_str());
			predicateQA = QCryptographicHash::hash(predicateQS.toAscii(),QCryptographicHash::Sha1 );
		}

		if(arguments==3){
			objectQS = QString::fromLocal8Bit(argument3.getName().c_str());
			objectQA = QCryptographicHash::hash(objectQS.toAscii(),QCryptographicHash::Sha1 );
		}else{
			predicateQS = QString::fromLocal8Bit(argument2.getName().c_str());
			predicateQA = QCryptographicHash::hash(predicateQS.toAscii(),QCryptographicHash::Sha1 );
		}

		s_Co = (uint)subjectQA[0] % node_len;   //S
		p_Co = (uint)predicateQA[0] % node_len; //p
		o_Co = (uint)objectQA[0] % node_len; 	//O

		s_p_Co = (s_Co + p_Co) % node_len;		//s+p
		s_o_Co = (s_Co + o_Co) % node_len;		//s+o
		p_o_Co = (p_Co + o_Co) % node_len;		//p+o

		if(arguments==3){
			if((argument1.getType()!=RDFS_VARIABLE)&&(argument2.getType()!=RDFS_VARIABLE)&&(argument3.getType()!=RDFS_VARIABLE)){
				coordinate[0]=s_Co;			//s p o
				coordinate[1]=p_Co;
				coordinate[2]=o_Co;
			}else if((argument1.getType()==RDFS_VARIABLE)&&(argument2.getType()!=RDFS_VARIABLE)&&(argument3.getType()!=RDFS_VARIABLE)){
				coordinate[0]=p_o_Co;		//?s p o  coordinate: p+o p o
				coordinate[1]=p_Co;
				coordinate[2]=o_Co;
			}else if((argument1.getType()!=RDFS_VARIABLE)&&(argument2.getType()==RDFS_VARIABLE)&&(argument3.getType()!=RDFS_VARIABLE)){
				coordinate[0]=s_Co;		//s ?p o  coordinate: s s+o o
				coordinate[1]=s_o_Co;
				coordinate[2]=o_Co;
			}else if((argument1.getType()!=RDFS_VARIABLE)&&(argument2.getType()!=RDFS_VARIABLE)&&(argument3.getType()==RDFS_VARIABLE)){
				coordinate[0]=s_Co;		//s p ?o  coordinate: s p s+p
				coordinate[1]=p_Co;
				coordinate[2]=s_p_Co;
			}else if((argument1.getType()!=RDFS_VARIABLE)&&(argument2.getType()==RDFS_VARIABLE)&&(argument3.getType()==RDFS_VARIABLE)){
				coordinate[0]=s_Co;		//s ?p ?o  coordinate: s s s
				coordinate[1]=s_Co;
				coordinate[2]=s_Co;
			}else if((argument1.getType()==RDFS_VARIABLE)&&(argument2.getType()!=RDFS_VARIABLE)&&(argument3.getType()==RDFS_VARIABLE)){
				coordinate[0]=p_Co;		//?s p ?o  coordinate: p p p
				coordinate[1]=p_Co;
				coordinate[2]=p_Co;
			}else if((argument1.getType()==RDFS_VARIABLE)&&(argument2.getType()==RDFS_VARIABLE)&&(argument3.getType()!=RDFS_VARIABLE)){
				coordinate[0]=o_Co;		//?s ?p o  coordinate: o o o
				coordinate[1]=o_Co;
				coordinate[2]=o_Co;
			}else{ //?s ?p ?o
				coordinate[0]=-1;
				coordinate[1]=-1;
				coordinate[2]=-1;
			}
		}else{			//arguments==2
			if((argument1.getType()==RDFS_VARIABLE)&&(argument2.getType()==RDFS_VARIABLE)){ //?s p ?o   p p p
				coordinate[0]=p_Co;
				coordinate[1]=p_Co;
				coordinate[2]=p_Co;
			}else if((argument1.getType()!=RDFS_VARIABLE)&&(argument2.getType()!=RDFS_VARIABLE)){ // s p o s p o
				coordinate[0]=s_Co;
				coordinate[1]=p_Co;
				coordinate[2]=o_Co;
			}else if((argument1.getType()!=RDFS_VARIABLE)&&(argument2.getType()==RDFS_VARIABLE)){ // s p ?o s p s+p
				coordinate[0]=s_Co;
				coordinate[1]=p_Co;
				coordinate[2]=s_p_Co;
			}else if((argument1.getType()==RDFS_VARIABLE)&&(argument2.getType()!=RDFS_VARIABLE)){ // ?s p o p+o p o
				coordinate[0]=p_o_Co;
				coordinate[1]=p_Co;
				coordinate[2]=o_Co;
			}
		}
		coordinate[0]=0;
		coordinate[1]=0;
		coordinate[2]=0;
		return coordinate;
	}

	int applySubstitution(Binding sub){
		if(predicateName.compare("triple")){
			std::cout << "Not correct substitution!" << std::endl;
			return -1;
		}
		if(argument1.getType()==RDFS_VARIABLE && !argument1.getName().compare(sub.getVariable())){
			Term temp(sub.getValue());
			argument1 = temp;
			return 0;
		}else if (argument2.getType()==RDFS_VARIABLE && !argument2.getName().compare(sub.getVariable())){
			Term temp(sub.getValue());
			argument2 = temp;
			return 1;
		}else if (argument3.getType()==RDFS_VARIABLE && !argument3.getName().compare(sub.getVariable())){
			Term temp(sub.getValue());
			argument3 = temp;
			return 2;
		}
		return -1;
	}

	bool noVars(){
		if(arguments==2){
			if(!(argument1.getType()==RDFS_VARIABLE) && !(argument2.getType()==RDFS_VARIABLE)){
				return true;
			}
		}else if(arguments==3){
			if(!(argument1.getType()==RDFS_VARIABLE) && !(argument2.getType()==RDFS_VARIABLE) && !(argument3.getType()==RDFS_VARIABLE)){
				return true;
			}
		}
		return false;
	}

	std::string createSparqlGraphPattern(){
		std::string subquery_cmd;
		std::string subj_ele;
		std::string pred_ele;
		std::string obj_ele;
		if(this->arguments==2){
			if(argument1.getType()==RDFS_VARIABLE){//variable

				subj_ele=argument1.getName();
			}else{
				subj_ele="<"+argument1.getName()+">";
			}
			if(argument2.getType()==RDFS_VARIABLE){

				obj_ele=argument2.getName();
			}else{
				obj_ele="<"+argument2.getName()+">";
			}
			if(!predicateName.compare("subClass"))
				pred_ele="<http://www.w3.org/2000/01/rdf-schema#subClassOf>";
			else if(!predicateName.compare("subProperty"))
				pred_ele="<http://www.w3.org/2000/01/rdf-schema#subPropertyOf>";
			else if(!predicateName.compare("type"))
				pred_ele="<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>";
			else
				return "error";
		}else{
			if(argument1.getType()==RDFS_VARIABLE){

				subj_ele=argument1.getName();
			}else{
				subj_ele="<"+argument1.getName()+">";
			}
			if(argument2.getType()==RDFS_VARIABLE){

				pred_ele=argument2.getName();
			}else{
				pred_ele="<"+argument2.getName()+">";
			}
			if(argument3.getType()==RDFS_VARIABLE){

				obj_ele=argument3.getName();
			}else{
				obj_ele="<"+argument3.getName()+">";
			}
		}
		subquery_cmd = subj_ele+ " " + pred_ele+ " " + obj_ele;
		return subquery_cmd;
	}

	Term subjectPattern(){
		return argument1;
	}

	Term predicatePattern(){
		if(!predicateName.compare("subClass")){
			predicateNode.setName("http://www.w3.org/2000/01/rdf-schema#subClassOf");
			predicateNode.setType(RDFS_ATOM);
		}
		else if(!predicateName.compare("subProperty")){
			predicateNode.setName("http://www.w3.org/2000/01/rdf-schema#subPropertyOf");
			predicateNode.setType(RDFS_ATOM);
		}
		else if(!predicateName.compare("type")){
			predicateNode.setName("http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
			predicateNode.setType(RDFS_ATOM);
		}else {
			predicateNode=argument2;
		}
		return predicateNode;
	}

	Term objectPattern(){
		if(arguments==2)
			return argument2;
		else
			return argument3;
	}

	bool match(Statement s){
	    return( subjectPattern().match( s.getSubject() ) &&
	            predicatePattern().match( s.getPredicate() ) &&
	            objectPattern().match( s.getObject() ) );
	}

};



#endif //_PREDICATE_H_
